home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Diamond Collection / The Diamond Collection (Software Vault)(Digital Impact).ISO / cdr47 / sb16snd.zip / SBIO.PAS < prev    next >
Pascal/Delphi Source File  |  1995-02-18  |  10KB  |  323 lines

  1. {           Copyright 1995 by Ethan Brodsky.  All rights reserved.           }
  2. unit SBIO;
  3.   interface
  4.     type
  5.       Mode = (Input, Output);
  6.     function  Init(BaseIO: word; IRQ: byte; DMA16: byte; IO: Mode; Rate: word): boolean;
  7.     procedure Shutdown;
  8.  
  9.     procedure StartIO(Length: LongInt);
  10.  
  11.     procedure SetHandler(Ptr: pointer);
  12.  
  13.     procedure GetBuffer(var BufPtr: pointer; Length: word);
  14.     procedure FreeBuffer(var BufPtr: pointer);
  15.  
  16.     function DMACount: word;
  17.  
  18.     var
  19.       IntCount : LongInt;
  20.       Done     : boolean;
  21.       CurBlock : byte;
  22.   implementation
  23.     uses
  24.       DOS;
  25.     var
  26.       ResetPort        : word;
  27.       ReadPort         : word;
  28.       WritePort        : word;
  29.       PollPort         : word;
  30.       Poll16Port       : word;
  31.  
  32.       PICRotatePort    : word;
  33.       PICMaskPort      : word;
  34.  
  35.       DMAMaskPort      : word;
  36.       DMAClrPtrPort    : word;
  37.       DMAModePort      : word;
  38.       DMABaseAddrPort  : word;
  39.       DMACountPort     : word;
  40.       DMAPagePort      : word;
  41.  
  42.       IRQStartMask     : byte;
  43.       IRQStopMask      : byte;
  44.       IRQIntVector     : byte;
  45.       IntController    : byte;
  46.  
  47.       DMAStartMask     : byte;
  48.       DMAStopMask      : byte;
  49.       DMAMode          : byte;
  50.  
  51.       OldIntVector     : pointer;
  52.       OldExitProc      : pointer;
  53.  
  54.       HandlerInstalled : boolean;
  55.  
  56.       MemArea          : pointer;
  57.       MemAreaSize      : LongInt;
  58.       BufferAddress    : LongInt;
  59.       BufferPage       : byte;
  60.       BufferOffset     : word;
  61.       BufferLength     : word;
  62.       BlockLength      : word;
  63.  
  64.       IOMode           : Mode;
  65.  
  66.       SamplesRemaining : LongInt;
  67.       SamplingRate     : word;
  68.  
  69.     type
  70.       HandlerProc = procedure;
  71.     var
  72.       Handler: HandlerProc;
  73.  
  74.    {Low level sound card I/O ════════════════════════════════════════════════}
  75.     procedure WriteDSP(Value: byte);
  76.       begin
  77.         repeat until (Port[WritePort] and $80) = 0;
  78.         Port[WritePort] := Value;
  79.       end;
  80.  
  81.     function ReadDSP: byte;
  82.       begin
  83.         repeat until (Port[PollPort] and $80) <> 0;
  84.         ReadDSP := Port[ReadPort];
  85.       end;
  86.  
  87.     function ResetDSP: boolean;
  88.       var
  89.         i: byte;
  90.       begin
  91.         Port[ResetPort] := 1;
  92.         Port[ResetPort] := 0;
  93.         i := 100;
  94.         while (ReadDSP <> $AA) and (i > 0) do Dec(i);
  95.         if i > 0
  96.           then ResetDSP := true
  97.           else ResetDSP := false;
  98.       end;
  99.  
  100.     function DMACount: word;
  101.       var
  102.         x: word;
  103.       begin
  104.         x := Port[DMACountPort];
  105.         x := x + Port[DMACountPort] * 256;
  106.  
  107.         DMACount := x;
  108.       end;
  109.  
  110.    {Initialization and shutdown ═════════════════════════════════════════════}
  111.     procedure InstallHandler; forward;
  112.     procedure UninstallHandler; forward;
  113.  
  114.     function  Init(BaseIO: word; IRQ: byte; DMA16: byte; IO: Mode; Rate: word): boolean;
  115.       begin
  116.        {Sound card IO ports}
  117.         ResetPort  := BaseIO + $6;
  118.         ReadPort   := BaseIO + $A;
  119.         WritePort  := BaseIO + $C;
  120.         PollPort   := BaseIO + $E;
  121.         Poll16Port := BaseIO + $F;
  122.  
  123.         Init := true;
  124.        {Reset DSP, get version, and pick output mode}
  125.         if not(ResetDSP)
  126.           then
  127.             begin
  128.               Init := false;
  129.               Exit;
  130.             end;
  131.  
  132.        {Compute interrupt ports and parameters}
  133.         if IRQ <= 7
  134.           then
  135.             begin
  136.               IRQIntVector  := $08+IRQ;
  137.               PICRotatePort := $20;
  138.               PICMaskPort   := $21;
  139.               IntController := 1;
  140.             end
  141.           else
  142.             begin
  143.               IRQIntVector  := $70+IRQ-8;
  144.               PICRotatePort := $A0;
  145.               PICMaskPort   := $A1;
  146.               IntController := 2;
  147.             end;
  148.         IRQStopMask  := 1 shl (IRQ mod 8);
  149.         IRQStartMask := not(IRQStopMask);
  150.  
  151.        {Compute DMA ports and parameters}
  152.         DMAMaskPort     := $D4;
  153.         DMAClrPtrPort   := $D8;
  154.         DMAModePort     := $D6;
  155.         DMABaseAddrPort := $C0 + 4*(DMA16-4);
  156.         DMACountPort    := $C2 + 4*(DMA16-4);
  157.         case DMA16
  158.           of
  159.             5:  DMAPagePort := $8B;
  160.             6:  DMAPagePort := $89;
  161.             7:  DMAPagePort := $8A;
  162.           end;
  163.         DMAStopMask  := DMA16-4 + $04;   {000001xx}
  164.         DMAStartMask := DMA16-4 + $00;   {000000xx}
  165.         if IO = Input
  166.           then DMAMode := DMA16-4 + $54  {010101xx (Input)  }
  167.           else DMAMode := DMA16-4 + $58; {010110xx (Output) }
  168.  
  169.         IOMode := IO;
  170.         SamplingRate := Rate;
  171.  
  172.         InstallHandler;
  173.       end;
  174.  
  175.     procedure Shutdown;
  176.       begin
  177.         UninstallHandler;
  178.         ResetDSP;
  179.       end;
  180.  
  181.    {Start and stop input/output ═════════════════════════════════════════════}
  182.     procedure StartIO(Length: LongInt);
  183.       begin
  184.         Done := false;
  185.         SamplesRemaining := Length;
  186.         CurBlock := 1;
  187.  
  188.        {Program DMA controller}
  189.         Port[DMAMaskPort]     := DMAStopMask;
  190.         Port[DMAClrPtrPort]   := $00;
  191.         Port[DMAModePort]     := DMAMode;
  192.         Port[DMABaseAddrPort] := Lo(BufferOffset);
  193.         Port[DMABaseAddrPort] := Hi(BufferOffset);
  194.         Port[DMACountPort]    := Lo(BufferLength - 1);
  195.         Port[DMACountPort]    := Hi(BufferLength - 1);
  196.         Port[DMAPagePort]     := BufferPage;
  197.         Port[DMAMaskPort]     := DMAStartMask;
  198.  
  199.        {Program sound card}
  200.         if IOMode = Output
  201.           then WriteDSP($41)       {Set digitized sound output sampling rate }
  202.           else WriteDSP($42);      {Set digitized sound input sampling rate  }
  203.         WriteDSP(Hi(SamplingRate));
  204.         WriteDSP(Lo(SamplingRate));
  205.         if IOMode = Output
  206.           then WriteDSP($B6)       {DSP command:  16-bit D/A, Auto-Init, FIFO}
  207.           else WriteDSP($BE);      {DSP command:  16-bit A/D, Auto-Init, FIFO}
  208.         WriteDSP($10);             {DMA mode:     16-bit Signed Mono         }
  209.         WriteDSP(Lo(BlockLength - 1));
  210.         WriteDSP(Hi(BlockLength - 1));
  211.       end;
  212.  
  213.    {Interrupt handling ══════════════════════════════════════════════════════}
  214.      procedure SetHandler(Ptr: pointer);
  215.        begin
  216.          Handler := HandlerProc(Ptr);
  217.        end;
  218.      procedure ToggleBlock;
  219.        begin
  220.          if CurBlock = 1
  221.            then CurBlock := 2
  222.            else CurBlock := 1;
  223.        end;
  224.  
  225.      procedure IntHandler; interrupt;
  226.        var
  227.          Temp: byte;
  228.        begin {CurBlock -> Block that just finished being output}
  229.          Inc(IntCount);
  230.  
  231.          if @Handler <> nil then Handler;
  232.  
  233.          if SamplesRemaining > 0
  234.            then
  235.              Dec(SamplesRemaining, BlockLength)
  236.            else
  237.              begin
  238.                Done := true;
  239.                WriteDSP($D9);
  240.              end;
  241.          ToggleBlock; {CurBlock -> Block that just started}
  242.  
  243.          Temp := Port[Poll16Port];
  244.          Port[$20] := $20;
  245.        end;
  246.  
  247.     procedure EnableInterrupts;  InLine($FB); {STI}
  248.     procedure DisableInterrupts; InLine($FA); {CLI}
  249.  
  250.     procedure InstallHandler;
  251.       begin
  252.         DisableInterrupts;
  253.         Port[PICMaskPort] := Port[PICMaskPort] or IRQStopMask;
  254.         GetIntVec(IRQIntVector, OldIntVector);
  255.         SetIntVec(IRQIntVector, @IntHandler);
  256.         Port[PICMaskPort] := Port[PICMaskPort] and IRQStartMask;
  257.         EnableInterrupts;
  258.         HandlerInstalled := true;
  259.       end;
  260.  
  261.     procedure UninstallHandler;
  262.       begin
  263.         DisableInterrupts;
  264.         Port[PICMaskPort] := Port[PICMaskPort] or IRQStopMask;
  265.         SetIntVec(IRQIntVector, OldIntVector);
  266.         EnableInterrupts;
  267.         HandlerInstalled := false;
  268.       end;
  269.  
  270.    {Memory management ═══════════════════════════════════════════════════════}
  271.     function GetLinearAddr(Ptr: pointer): LongInt;
  272.       begin
  273.         GetLinearAddr := LongInt(Seg(Ptr^))*16 + LongInt(Ofs(Ptr^));
  274.       end;
  275.  
  276.     function NormalizePtr(p: pointer): pointer;
  277.       var
  278.         LinearAddr: LongInt;
  279.       begin
  280.         LinearAddr := GetLinearAddr(p);
  281.         NormalizePtr := Ptr(LinearAddr div 16, LinearAddr mod 16);
  282.       end;
  283.  
  284.     procedure GetBuffer(var BufPtr: pointer; Length: word);
  285.       begin
  286.        {Find a block of memory that does not cross a page boundary}
  287.         MemAreaSize := 8 * Length;
  288.         GetMem(MemArea, MemAreaSize);
  289.         if MemArea = nil then Halt;
  290.         if ((GetLinearAddr(MemArea) div 2) mod 65536)+Length*2 < 65536
  291.           then BufPtr := MemArea
  292.           else BufPtr := NormalizePtr(Ptr(Seg(MemArea^), Ofs(MemArea^)+4*Length));
  293.  
  294.        {DMA parameters}
  295.         BufferAddress := GetLinearAddr(BufPtr);
  296.         BlockLength   := Length;  BufferLength := Length*2;
  297.         BufferPage   := BufferAddress div 65536;
  298.         BufferOffset := (BufferAddress div 2) mod 65536;
  299.       end;
  300.  
  301.     procedure FreeBuffer(var BufPtr: pointer);
  302.       begin
  303.         BufPtr := nil;
  304.         FreeMem(MemArea, MemAreaSize);
  305.       end;
  306.  
  307.  
  308.    {Emergency shutdown ══════════════════════════════════════════════════════}
  309.     procedure SBExitProc; far; {Called automatically on program termination}
  310.       begin
  311.         ExitProc := OldExitProc;
  312.         Port[$20] := $20;
  313.         WriteDSP($D5);
  314.         Port[DMAMaskPort] := DMAStopMask;
  315.         if HandlerInstalled then UninstallHandler;
  316.       end;
  317.  
  318.   begin
  319.     IntCount    := 0;
  320.     OldExitProc := ExitProc;
  321.     ExitProc    := @SBExitProc;
  322.     Handler     := nil
  323.   end.